/************************************************************************************
**  
* @copyright (c) 2015-, Technology Co., LTD. All Right Reserved.
*
*************************************************************************************/
/**
* @file     duye_cap_filter_engine.cpp
* @version     
* @brief      
* @author   
* @date     2013-04-02
* @note 
*
*  1. 2018-04-02 created this file
* 
*/
#include <duye_cap_filter_engine.h>

// static const int8* DUYE_LOG_PREFIX = "duye.cap.fileter.engine";

namespace duye {

bool FilterEngine::filterPkt(const PacketReference& pktRef, FilterRule* rule) {
	if (rule == NULL || pktRef.isNull())
		return true;

	if (rule->getFilterWorker() == NULL)
		return generalFilter(pktRef, *rule);

	return rule->getFilterWorker()->filterPkt(pktRef, *rule);
}

bool FilterEngine::generalFilter(const PacketReference& pktRef, const FilterRule& rule) {
	// white table filter
	if (!whiteRuleFilter(pktRef, rule.whiteRuleList())) {
		return false;
	}

	// black table filter
	if (!blackRuleFilter(pktRef, rule.blackRuleList())) {
		return false;
	}

	return true;
}

bool FilterEngine::whiteRuleFilter(const PacketReference& pktRef, const RuleItemList& whiteRuleList) {
	bool whiteRet = false;
	for (RuleItemList::const_iterator iter = whiteRuleList.begin(); iter != whiteRuleList.end(); ++iter) {
		const RuleItem& item = *iter;

		// protocol filter
		if (item.hasProtoFilter()) {
			if (!(item.protocol() == pktRef->getLinkLayerProto() ||
				item.protocol() == pktRef->getNetLayerProto() ||
				item.protocol() == pktRef->getTfLayerProto())) {
				continue;
			}
		}

		// ip filter
		if (item.hasIPAddrFilter()) {
			if (!((item.srcIPAddr() != 0 && item.srcIPAddr() == pktRef->getSrcIP() &&
				item.dstIPAddr() != 0 && item.dstIPAddr() == pktRef->getDstIP()) || 
				(item.srcIPAddr() != 0 && item.srcIPAddr() == pktRef->getDstIP() &&
				item.dstIPAddr() != 0 && item.dstIPAddr() == pktRef->getSrcIP()))) {
				continue;
			}
		}

		// port filter
		if (item.hasPortFilter()) {
			if (!(((item.srcPort() == 0 || item.srcPort() == pktRef->getSrcPort()) &&
				(item.dstPort() == 0 || item.dstPort() == pktRef->getDstPort())) || 
				((item.srcPort() == 0 || item.srcPort() == pktRef->getDstPort()) &&
				(item.dstPort() == 0 || item.dstPort() == pktRef->getSrcPort())))) {
				continue;
			}
		}

		// ip range filter
		if (item.hasRangeIPAddrFilter()) {
			const IPRangePair& srcIPRange = item.srcIPRange();

			if (!(srcIPRange.first != 0 && 
				srcIPRange.second != 0 && 
				srcIPRange.first < srcIPRange.second)) {
				continue;
			}

			if (!(pktRef->getSrcIP() >= srcIPRange.first &&
				pktRef->getSrcIP() <= srcIPRange.second)) {
				continue;
			}

			const IPRangePair& dstIPRange = item.dstIPRange();
			if (!(dstIPRange.first != 0 && 
				dstIPRange.second != 0 && 
				dstIPRange.first < dstIPRange.second)) {
				continue;
			}

			if (!(pktRef->getDstIP() >= dstIPRange.first &&
				pktRef->getDstIP() <= dstIPRange.second)) {
				continue;
			}
		}

		// port range filter
		if (item.hasRangePortFilter()) {
			const PortRangePair& srcPortRange = item.srcPortRange();
			if (!(srcPortRange.first != 0 && 
				srcPortRange.second != 0 && 
				srcPortRange.first < srcPortRange.second)) {
				continue;
			}

			if (!(pktRef->getSrcPort() >= srcPortRange.first &&
				pktRef->getSrcPort() <= srcPortRange.second)) {
				continue;
			}

			const PortRangePair& dstPortRange = item.dstPortRange();
			if (!(dstPortRange.first != 0 && 
				dstPortRange.second != 0 && 
				dstPortRange.first < dstPortRange.second)) {
				continue;
			}

			if (!(pktRef->getDstPort() >= dstPortRange.first &&
				pktRef->getDstPort() <= dstPortRange.second)) {
				continue;
			}
		}

		whiteRet = true;
		break;
	}

	return whiteRet;
}

bool FilterEngine::blackRuleFilter(const PacketReference& pktRef, const RuleItemList& blackRuleList) {
	bool blackRet = true;
	for (RuleItemList::const_iterator iter = blackRuleList.begin(); iter != blackRuleList.end(); ++iter) {
		const RuleItem& item = *iter;

		blackRet = false;

		// protocol filter
		if (item.hasProtoFilter()) {
			if (item.protocol() == pktRef->getLinkLayerProto() ||
				item.protocol() == pktRef->getNetLayerProto() ||
				item.protocol() == pktRef->getTfLayerProto()) {
				break;
			}
		}

		// ip filter
		if (item.hasIPAddrFilter()) {
			if ((item.srcIPAddr() != 0 && item.srcIPAddr() == pktRef->getSrcIP() &&
				item.dstIPAddr() != 0 && item.dstIPAddr() == pktRef->getDstIP()) || 
				(item.srcIPAddr() != 0 && item.srcIPAddr() == pktRef->getDstIP() &&
				item.dstIPAddr() != 0 && item.dstIPAddr() == pktRef->getSrcIP())) {
				break;
			}
		}

		// port filter
		if (item.hasPortFilter()) {
			if (((item.srcPort() == 0 || item.srcPort() == pktRef->getSrcPort()) &&
				(item.dstPort() == 0 || item.dstPort() == pktRef->getDstPort())) || 
				((item.srcPort() == 0 || item.srcPort() == pktRef->getDstPort()) &&
				(item.dstPort() == 0 || item.dstPort() == pktRef->getSrcPort()))) {
				break;
			}
		}

		// ip range filter
		if (item.hasRangeIPAddrFilter()) {
			const IPRangePair& srcIPRange = item.srcIPRange();

			if (srcIPRange.first != 0 && 
				srcIPRange.second != 0 && 
				srcIPRange.first < srcIPRange.second) {
				break;
			}

			if (pktRef->getSrcIP() >= srcIPRange.first &&
				pktRef->getSrcIP() <= srcIPRange.second) {
				break;
			}

			const IPRangePair& dstIPRange = item.dstIPRange();
			if (dstIPRange.first != 0 && 
				dstIPRange.second != 0 && 
				dstIPRange.first < dstIPRange.second) {
				break;
			}

			if (pktRef->getDstIP() >= dstIPRange.first &&
				pktRef->getDstIP() <= dstIPRange.second) {
				break;
			}
		}

		// port range filter
		if (item.hasRangePortFilter()) {
			const PortRangePair& srcPortRange = item.srcPortRange();
			if (srcPortRange.first != 0 && 
				srcPortRange.second != 0 && 
				srcPortRange.first < srcPortRange.second) {
				break;
			}

			if (pktRef->getSrcPort() >= srcPortRange.first &&
				pktRef->getSrcPort() <= srcPortRange.second) {
				break;
			}

			const PortRangePair& dstPortRange = item.dstPortRange();
			if (dstPortRange.first != 0 && 
				dstPortRange.second != 0 && 
				dstPortRange.first < dstPortRange.second) {
				break;
			}

			if (pktRef->getDstPort() >= dstPortRange.first &&
				pktRef->getDstPort() <= dstPortRange.second) {
				break;
			}
		}

		blackRet = true;
	}

	return blackRet;
}

}
